iT邦幫忙

2024 iThome 鐵人賽

DAY 6
1
AI/ ML & Data

一個Kaggle金牌解法是如何誕生的?跟隨Kaggle NLP競賽高手的討論,探索解題脈絡系列 第 6

[Day 6] 別著急訓練模型,挖掘好用的 feature 是成功的一半: EDA 實戰演練(上)

  • 分享至 

  • xImage
  •  

在一個充滿活力的校園里,安娜老師是深受學生喜愛的教師。但是這幾年她發現,在這個信息爆炸的時代,擁有良好的摘要能力變得尤為重要。然而,許多學生在總結時往往抓不住重點,或者表達不夠清晰流暢。於是安娜老師開始加強練習學生摘要總結的能力。但每當周末來臨,她的笑容就會變得有些勉強——因為那意味著又有一堆學生們的摘要練習等著她去批改。

今天要介紹的比賽,是去年2023年7月舉辦的、為期也是3個月左右的 kaggle 競賽,CommonLit - Evaluate Student Summaries,一樣也是由 The Learning Agency Lab 發起的比賽。不過,前幾天介紹的是「議論文」相關的自動打分系統,這次的是摘要寫作的評分,相比前一個只要針對學生寫的文章內容獨立評分即可;這次還要參考學生摘要的對象,也就是原文,再去比對這個摘要內容是否有摘述到原文的重點與精髓,再去評價行文的流暢度邏輯性等等。

不得不說,安娜老師真的是很會善用科技的力量來減輕自己工作的負荷,實乃我輩打工人的楷模xdd

下面是本賽題主辦方提供的訓練與測試資料簡介:

  1. summaries_train.csv - 訓練集中的摘要,共有 7165 筆 data,a每筆 data 都來自不同的學生所寫,包含下面 column:
  • student_id - 學生作者的ID。
  • prompt_id - 與題目相連的提示ID。
  • text - 學生摘要的完整文本。
  • content - 摘要的內容得分。第一個目標。介在-2~4之間,是連續的數值,應該是由多個評分者的平均分所得。
  • wording - 摘要的措詞得分。第二個目標。介在-2~4之間,是連續的數值,應該是由多個評分者的平均分所得。

summaries_train.csv 的前三筆資料截圖如下:
https://ithelp.ithome.com.tw/upload/images/20240920/201526683ridGmcjfM.png

特別說明一下,這邊的 prompt 指的是題目的意思,也就是學生在寫摘要前要參考的題幹。這個題幹會包含要摘要的對象也就是原文的標題、原文的內容、以及要從哪些面向摘要,或說摘要的方向要求,也可以說是學生被要求要回答的具體問題。

  1. summaries_test.csv - 測試集中的摘要。包含上述所有字段,除了content和wording score。

  2. prompts_train.csv - 只包含 4 個 training set 所使用到的題幹說明文件,包含下面幾個 column:

  • prompt_id - 與摘要文件相關聯的題幹的ID。
  • prompt_question - 學生被要求回答的具體問題。
  • prompt_title - 題幹的標題。
  • prompt_text - 完整的題幹文字。

https://ithelp.ithome.com.tw/upload/images/20240920/20152668JmliwxO4b7.png

  1. prompts_test.csv - 測試集的題幹說明文件。包含與上面相同的column。這份文件只是一個例子。完整的測試集有大量的題目(prompt)。

值得注意的是,trainset/testset/hidden testset之間不共享任何prompt(題目)。

  1. sample_submission.csv - 正確格式的提交文件。具體詳情見評估頁面。

整個 dataset(包含所有訓練和測試資料),大約有24,000篇由三年級到十二年級學生撰寫的摘要,涵蓋了多種主題和類型的文章。其中訓練資料佔了 7165筆,測試資料佔了大約 17000 筆。

值得注意的是,我們透過觀察summaries_train.csvprompt_id,發現只有4個不同的 prompt_idprompt_train.csv也只有4筆資料,分別說明這四個題幹的內容與要求)。

但是!

「完整的測試集有大量的題目(prompt)」 這句話很重要呦(根據可靠消息,大概來自 122 個 prompts)!

這代表我們無法取得的測試資料其實是非常多元的、來自不同題目的要求,並且還和訓練資料沒有任何一題重疊,所以我們的 model 要如何從訓練資料 transfer 到測試資料會是很大的挑戰。

由於這次比賽有兩個要預測的目標:content score(摘要的整體內如分數,包含是否有完整摘述原文重點),以及 wording score(摘要的措辭分數),因此本次比賽採用:

MCRMSE,即均方根誤差的列均值(Mean Columnwise Root Mean Squared Error):

https://ithelp.ithome.com.tw/upload/images/20240920/20152668hbIYWdjcDa.png

其中 (N_t) 是評分的真實目標列的數量,(y) 和 (\hat{y}) 分別是實際值和預測值。

這個指標評估的是模型預測值與實際值之間的均方根誤差,但進行了平均處理以考慮多個目標列。首先計算每個目標列的均方誤差,然後對每列的均方誤差求平方根,最後對所有列的結果取平均。這樣能夠提供一個整體的衡量模型在多個目標上預測性能的指標。

另外本次比賽和前幾天介紹的比賽一樣,都是 code competition,也就是無法拿到完整的 test data 的 input,只能上傳 code 去操作放在系統後台的 test data。這種比賽還需要額外注意他的運算資源限制:

  • CPU Notebook <= 9 hours run-time
  • GPU Notebook <= 9 hours run-time

我們之後要評估每個sample平均的字數長度、inference的時間,來決定我們到底可以ensemble幾個模型,才不會在測試的時候超時。

也許你會好奇,明明這個比賽相比前幾天介紹的Automated Essay Scoring 2.0早發布,為什麼不先介紹這個比賽呢?

因為這個比賽的許多參賽者有使用 LLM 擴增資料等技巧,我認為相比前一個比賽來說會更複雜一些,所以挪到第六天再來介紹~

目前為止,你已經了解比賽的目標、資料的數量及其 input 與 output 的格式了,還有評分所使用的 metric 了。


❓❓可以暫停一下,思考看看,如果你是參賽者,你會如何設計你的第一個解題方案呢?你的第一步是什麼❓❓


在開始前,還是再次解釋一下之後會時常出現的 "CV" 和 "LB" 這兩個詞到底是什麼意思? 我第一次看 Kaggle 討論區時,常常看人家在討論他們但搞不懂在指什麼。

💡 CV vs LB?

CV分數指的是我們在訓練集上通過交叉驗證(Cross-Validation)得到的(MCRMSE)分數。本次比賽的一個重要點是,無論是 hidden testset還是public test set都包含了我們的模型從未見過的prompt。因此,我們需要采用一種交叉驗證方法,確保模型在驗證時面對的是之前未曾接觸過的prompt。之前提過的“GroupKFold”交叉驗證就是一個解決方案,在每次折疊中,你將一個特定“prompt”的數據留作驗證集,其余數據用作訓練集。

LB分數是指我們在Leadernoard所獲得的public score。由於這個分數僅基於實際測試數據的13%進行評估,而private score則使用剩下的87%,所以我們不應該過度優化LB分數以追求在排行榜上的好位置。相反,我們應該努力使你的交叉驗證(CV)分數成為排行榜分數的良好指標,並盡量優化CV分數,縮小 CV 和 LB 的差距。

(以上說明改寫自1)

🎯 Enjoy the "discovery" and "learning" process

也許在看過前面幾天我們介紹過的做法後,你會想,不管了,直接 deberta-v3 large 拿過來,繼續爆 train 一波再說。

但是這次你的 input 要是什麼呢?之前只有 essay 的 context 可以用,這次除了有學生的 summary text,還有 prmpt 的相關資訊,如:promt_title, prompt_question, prompt_text等等,你要怎麼結合?全部當作input concatenate起來丟進去嗎?還是只需要其中某部分即可呢?

如果這些問題會讓你開始覺得有點手足無措、無法決定的話,EDA(Exploratory Data Analysis)作為第一步是個絕妙的好辦法!畢竟 Data Science 最重要的就是"理解我們手中的 data",在過程中享受發掘(discovery)和學習(learning)的樂趣。

EDA (基礎版)

為了讓自己和 data 混熟,我們藉由下面幾個問題開始探索數據(相關問題參考自 1,code主要參考討論區無私的分享2)
*** 🤔 How do a prompt title, prompt text, prompt question and corresponding student answer look like?**

print(f"prompt_id: {prompts.loc[1]['prompt_id']}\n")
print(f"prompt_question: {prompts.loc[1]['prompt_question']}\n")
print(f"prompt_title: {prompts.loc[1]['prompt_title']}\n")
print(f"prompt_text: {prompts.loc[1]['prompt_text'][:500]}... (truncated to 500 characters)\n")
print(f"Number of characters in the full prompt text: {len(prompts.loc[1]['prompt_text'])}")

https://ithelp.ithome.com.tw/upload/images/20240920/201526681KjhYrkUua.png

# Example summary
print(f"student_id: {summaries.loc[2]['student_id']}\n")
print(f"text: {summaries.loc[2]['text']}\n")
print(f"content: {summaries.loc[2]['content']}\n")
print(f"wording: {summaries.loc[2]['wording']}")

https://ithelp.ithome.com.tw/upload/images/20240920/201526689AV777k9pr.png

*** 🤔 How many prompts are in the training data?**

這個前面就提過了,只有 4 個 prompts 出現在 training data。

那 training data 中各有幾成的 data 屬於這些 prompt 呢?

# Frequency comparison of prompts in the training summaries data
labels = summaries['prompt_id'].value_counts().index
sizes = summaries['prompt_id'].value_counts().values
plt.pie(sizes, labels = labels, autopct = '%1.1f%%')
center_circle = plt.Circle((0,0), 0.3, color = 'white')
p = plt.gcf()
p.gca().add_artist(center_circle)
plt.show()

https://ithelp.ithome.com.tw/upload/images/20240920/20152668oikqKkVctE.png

分佈的還算平均,其中的大頭是 39c16e 這個 prompt 是關於 "On Tragedy" 的,共有 28.7% 的 training data 來自這個 prompt。

接下來我們把 prompt 和 training data 合併在一起,方便之後的分析:

merged = pd.merge(prompts, summaries, how = 'inner', on = 'prompt_id')
merged.head()
  • Are content and wording scores correlated?
    我們接下來分析 content 和 wording score 之間的相關性,初步猜測,應該 content 分數高的,word 分數也會高吧?兩者之間應該有一些正相關性吧?

來複習一下相關係數:

💡 Correlation Score(相關係數)

  • 什麽是相關係數?
    相關係數是一個統計指標,用來衡量兩個變量之間的線性關系強度和方向。最常用的相關系數是皮爾遜相關係數,它的值範圍從 -1 到 +1。

  • 計算方法
    皮爾森相關係數的計算公式為:

    r = (Σ(xi - x̄)(yi - ȳ)) / (√(Σ(xi - x̄)² * Σ(yi - ȳ)²))
    其中:

    xiyi 是兩個變量的觀測值。
    是這些觀測值的平均數。
    此公式實際上測量的是,兩個變量的偏離其各自均值的程度的乘積的平均值。分母是這些偏差的標準差的乘積,這確保了相關係數被標準化,使其在 -1 到 +1 的範圍內。

  • 分數代表的意義

    +1 的相關係數表示一個完美的正相關,即一個變量的增加總是與另一個變量的增加相對應。
    -1 的相關係數表示一個完美的負相關,即一個變量的增加總是與另一個變量的減少相對應。
    0 的相關係數表示兩個變量之間沒有線性關係。
    相關係數的絕對值越大,表示兩個變量之間的線性關係越強。
    例如:
    如果 r = 0.8 或 r = -0.8,這表明兩個變量之間存在較強的正向或負向關係。
    如果 r = 0.2 或 r = -0.2,則表示兩個變量之間的關係較弱。
    其他分數意義的參考:(reference to 3)
    https://ithelp.ithome.com.tw/upload/images/20240920/20152668ta2rOXVHBq.png

🤔 Content 和 Wording Score 有什麼樣的相關性呢?

  • 假設: content score 高的話,wording score 也會高。

下面的 code 首先會畫出 content score 的數量分佈,以及 wording sccore 的數量分佈。因為這兩個數值都是連續的,為了畫成直方圖的方式呈現,會用 math.floor(2 * (len(train)**(1/3))) + 1 的方式分 bin 來呈現。(其實這邊也可以用 density plot 來呈現)

# Distributions of the target variables and their relationship (training set)
fig, ax = plt.subplots(1, 3, figsize = (15, 5))
c, w, num_bin = train['content'], train['wording'], math.floor(2 * (len(train)**(1/3))) + 1
ax[0].hist(c, bins = num_bin), ax[1].hist(w, bins = num_bin), ax[2].scatter(c, w, marker = '.')
ax[0].set_xlabel("Content"), ax[1].set_xlabel("Wording"), ax[2].set_xlabel("Content")
ax[0].set_ylabel("Count"), ax[1].set_ylabel("Count"), ax[2].set_ylabel("Wording")
t0 = f"Mean: {c.mean():.2f}, SD: {c.std():.2f}, Skew: {c.skew():.2f}, Kurt: {c.kurt():.2f}"
t1 = f"Mean: {w.mean():.2f}, SD: {w.std():.2f}, Skew: {w.skew():.2f}, Kurt: {w.kurt():.2f}"
t2 = f"Correlation Coefficient: {c.corr(w):.2f}"
ax[0].set_title(t0), ax[1].set_title(t1), ax[2].set_title(t2)
plt.tight_layout()
plt.show()

🔎 Findings:
https://ithelp.ithome.com.tw/upload/images/20240920/201526684WeK17O0jl.png
從第三張圖可以看出,Wording 和 Content score 的相關係數有到 0.75,算是高度相關,初步驗證我們的假設。但這個特性我們該怎麼用,還沒有想法。
另外,word score 和 content score 的平均分在 -0.02, -0.06 ,中間偏低分。

另外我們可以執行:

df['content'].nunique(), df['wording'].nunique(), len(df[['content','wording']].drop_duplicates())  

結果是(1134, 1134, 1134),代表總共只有 1134 筆完全不同的 content score,以及完全不同的 wording score,並且如果某兩筆資料的 content 分數是相同的,那 wording 分數也一定會相同。

這說明 wording 和 content score 確實具有某種正相關性,但很難當作一個 feature,因為這種特性並不是連續的。

🤔 摘要寫的字數越多,分數會越高嗎?

這個...好像不一定吧,想起小時候寫的作文....:(圖片來源:4)
https://ithelp.ithome.com.tw/upload/images/20240920/20152668hkAmy2HDZ4.png

來確認一下吧!

定義這個通用function,可以把要觀察的 feature 統計好之後,存成 list 當成 x 傳入,再傳入這個 feature 的名稱給 name:

def feature_plots(x, name):
    fig, ax = plt.subplots(1, 3, figsize = (15, 5))
    c, w, num_bin = train['content'], train['wording'], math.floor(2 * (len(x)**(1/3))) + 1
    ax[0].hist(x, bins = num_bin)
    ax[1].scatter(x, c, marker = '.'), ax[2].scatter(x, w, marker = '.')
    ax[0].set_xlabel(name), ax[1].set_xlabel(name), ax[2].set_xlabel(name)
    ax[0].set_ylabel("Count"), ax[1].set_ylabel("Content"), ax[2].set_ylabel("Wording")
    t0 = f"Mean: {x.mean():.2f}, SD: {x.std():.2f}, Skew: {x.skew():.2f}, Kurt: {x.kurt():.2f}"
    t1 = f"Correlation Coefficient: {x.corr(c):.2f}"
    t2 = f"Correlation Coefficient: {x.corr(w):.2f}"
    ax[0].set_title(t0), ax[1].set_title(t1), ax[2].set_title(t2)
    plt.tight_layout()
    plt.show()

開始畫:

# Number of characters in summary texts
num_char_train = train['text'].str.len()
feature_plots(x = num_char_train, name = "Number of characters")

🔎 Findings:
https://ithelp.ithome.com.tw/upload/images/20240920/201526683mYJuif39m.png
字數和 content score 的相關係數有到 0.8,但是 wording score 的相關係數卻只有 0.54。可能的解釋是,拿高分的摘要,字數可能都不能太少;但有一些充字數的廢文卻在 wording score 上被狠狠地懲罰,拿了低分。


❓❓ 除了字數,我們還能找出什麼 feature 可能和 content / words score 相關呢? ❓❓


🤔 摘要中使用越多 stop words 分數會越低嗎?

英文的 "stop words" 指的是在文本處理中常常被忽略的那些詞,因為它們通常不承載重要的意義,也不利於搜索引擎索引和自然語言處理任務。這些詞包括助詞、介詞、連詞等,如 "the"、"is"、"at"、"which" 和 "on" 等。

stop words 越多可能代表越多冗詞贅字。

stops = stopwords.words("english")
num_stopwords_train = train['text'].apply(lambda x: len(set(x.split()) & set(stops)))
ratio_stopwords_train = num_stopwords_train / num_words_train
feature_plots(x = ratio_stopwords_train, name = "Ratio of stopwords")

🔎 Finding:
https://ithelp.ithome.com.tw/upload/images/20240920/201526680qVXXBdO8u.png
看起來還好,中度負相關。

其他還可以探討譬如說,頻繁使用名詞、動詞、形容詞的摘要,會不會是比較認真寫得高分摘要呢?這部分的討論大家有興趣的話,可以移步2的notebook看看!

或者可以計算摘要和原文的 rougeL score 或是計算摘要和原文的 Levenshtein Distance,說不定重疊的越多代表摘要改寫的越少,分數會越低?或是計算他們的相似度,相似度越高 content 分數會越高嗎?還有其他 feature 與 content/wording score 相關性的分析,我們明天會繼續討論。


❓❓除了上面提到的這些,你還能想到哪些有趣的問題呢❓❓


明天進階版本的 EDA,還會討論一些有點 tricky 的 feature,例如:把 content score rotate 30 度會發生什麼事情?(別鬧了,誰會想得到要這樣做啦!)。另外,在 [Day2] 曾經使用過的 Levenshtein Distance ,這次又要幫助我們發現一些有趣的事實了!

說不定挖掘到好的 feature 後,直接用這些 feature 訓練一個 LGBM, XGBOOST 之類 tree-based 的方法分數就超高了!根本不需要上 deberta 這種參數多訓練又慢的模型。

這邊先挖個坑,我們之後會再慢慢討論~LGBM至今仍然是kaggle競賽上被大量使用的技術喔,千萬別小看了它!嗯!👊👊

我們明天見!


謝謝讀到最後的你,如果喜歡這系列,別忘了按下訂閱,才不會錯過最新更新,也可以按讚給我鼓勵唷!
如果有任何回饋和建議,歡迎在留言區和我說✨✨


(Kaggle - CommonLit - Evaluate Student Summaries 解法分享系列)


上一篇
[Day 5] Kaggle 自動作文評分競賽(四)- 前四名優勝作法解析:彼此制衡的兩階段式微調、發揮奇效的 pseudo-labeling、集成多種 BERT Pooling 與最優化策略
下一篇
[Day 7]別著急訓練模型,挖掘好用的 feature 是成功的一半: EDA 實戰演練(中)- 從 Linguistic 和 Semantic 的角度計算文本相似度
系列文
一個Kaggle金牌解法是如何誕生的?跟隨Kaggle NLP競賽高手的討論,探索解題脈絡12
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言